{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 分析提取融合函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tvm\n",
    "from tvm import relay\n",
    "from tvm.relay.testing.synthetic import get_workload\n",
    "\n",
    "def get_conv_net():\n",
    "    \"\"\"获取 `fuse_ops.cc` 中描述情况的网络：\n",
    "\n",
    "            conv2d\n",
    "            /  |  \\\n",
    "           /   |   \\\n",
    "         op    op   op\n",
    "          \\    |    /\n",
    "           \\   |   /\n",
    "          elemwise add\n",
    "               |\n",
    "    \"\"\"\n",
    "    dshape = (1, 1, 5, 1)\n",
    "    x = relay.var(\"x\", shape=dshape)\n",
    "    y = relay.nn.conv2d(x, relay.var(\"w1\"), kernel_size=(3, 3), padding=(1, 1), channels=1)\n",
    "\n",
    "    x1 = relay.nn.conv2d(y, relay.var(\"w2\"), kernel_size=(3, 3), padding=(1, 1), channels=1)\n",
    "    x2 = relay.nn.conv2d(y, relay.var(\"w3\"), kernel_size=(3, 3), padding=(1, 1), channels=1)\n",
    "    x3 = relay.nn.conv2d(y, relay.var(\"w4\"), kernel_size=(3, 3), padding=(1, 1), channels=1)\n",
    "\n",
    "    z = relay.add(x1, x2)\n",
    "    z = relay.add(x3, z)\n",
    "\n",
    "    return tvm.IRModule.from_expr(z)\n",
    "\n",
    "\n",
    "def get_conv2d():\n",
    "    x = relay.var(\"x\", shape=(1, 56, 56, 64))\n",
    "    weight1 = relay.var(\"weight1\", shape=(3, 3, 64, 32))\n",
    "    y = relay.nn.conv2d(\n",
    "        x,\n",
    "        weight1,\n",
    "        channels=32,\n",
    "        kernel_size=(3, 3),\n",
    "        padding=(1, 1),\n",
    "        data_layout=\"NHWC\",\n",
    "        kernel_layout=\"HWIO\",\n",
    "    )\n",
    "    return tvm.IRModule.from_expr(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "def test_extract_identity():\n",
    "    mod = get_conv2d()\n",
    "    items = relay.analysis.extract_fused_functions(mod)\n",
    "    assert len(items) == 1\n",
    "\n",
    "    mod[\"main\"] = mod[\"main\"].with_attr(\"Primitive\", tvm.tir.IntImm(\"int32\", 1))\n",
    "    tvm.ir.structural_equal(list(items.values())[0], mod[\"main\"])\n",
    "\n",
    "\n",
    "def test_extract_conv_net():\n",
    "    mod = get_conv_net()\n",
    "    items = relay.analysis.extract_fused_functions(mod)\n",
    "    functions = list(items.values())\n",
    "    assert len(functions) == 2\n",
    "    x = functions[0]\n",
    "    y = functions[1]\n",
    "\n",
    "    def is_conv(func):\n",
    "        conv2d = relay.op.op.get(\"nn.conv2d\")\n",
    "        call_node = func.body\n",
    "        return call_node.op == conv2d\n",
    "\n",
    "    def is_conv_add(func):\n",
    "        add = relay.op.op.get(\"add\")\n",
    "        call_node = func.body\n",
    "        maybe_conv_module = tvm.IRModule.from_expr(call_node.args[0])\n",
    "        return call_node.op == add and is_conv(maybe_conv_module[\"main\"])\n",
    "\n",
    "    # Function traversal order isn't obvious, so checking both orders is more consistent\n",
    "    assert (is_conv(x) and is_conv_add(y)) or (is_conv_add(x) and is_conv(y))\n",
    "\n",
    "\n",
    "def test_extract_resnet():\n",
    "    mod, _params = get_workload()\n",
    "    items = relay.analysis.extract_fused_functions(mod)\n",
    "    assert len(items) == 7\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    test_extract_identity()\n",
    "    test_extract_conv_net()\n",
    "    test_extract_resnet()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "mod = get_conv_net()\n",
    "items = relay.analysis.extract_fused_functions(mod)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div class=\"highlight\" style=\"background: \"><pre style=\"line-height: 125%;\"><span></span><span style=\"color: #008000; font-weight: bold\">def</span> <span style=\"color: #AA22FF\">@main</span>(<span style=\"color: #AA22FF; font-weight: bold\">%</span>x: Tensor[(<span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">5</span>, <span style=\"color: #008000\">1</span>), float32], <span style=\"color: #AA22FF; font-weight: bold\">%</span>w1, <span style=\"color: #AA22FF; font-weight: bold\">%</span>w4, <span style=\"color: #AA22FF; font-weight: bold\">%</span>w2, <span style=\"color: #AA22FF; font-weight: bold\">%</span>w3) {\n",
       "  <span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">0</span> <span style=\"color: #AA22FF; font-weight: bold\">=</span> nn<span style=\"color: #AA22FF; font-weight: bold\">.</span>conv2d(<span style=\"color: #AA22FF; font-weight: bold\">%</span>x, <span style=\"color: #AA22FF; font-weight: bold\">%</span>w1, padding<span style=\"color: #AA22FF; font-weight: bold\">=</span>[<span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>], channels<span style=\"color: #AA22FF; font-weight: bold\">=</span><span style=\"color: #008000\">1</span>, kernel_size<span style=\"color: #AA22FF; font-weight: bold\">=</span>[<span style=\"color: #008000\">3</span>, <span style=\"color: #008000\">3</span>]);\n",
       "  <span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">1</span> <span style=\"color: #AA22FF; font-weight: bold\">=</span> nn<span style=\"color: #AA22FF; font-weight: bold\">.</span>conv2d(<span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">0</span>, <span style=\"color: #AA22FF; font-weight: bold\">%</span>w2, padding<span style=\"color: #AA22FF; font-weight: bold\">=</span>[<span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>], channels<span style=\"color: #AA22FF; font-weight: bold\">=</span><span style=\"color: #008000\">1</span>, kernel_size<span style=\"color: #AA22FF; font-weight: bold\">=</span>[<span style=\"color: #008000\">3</span>, <span style=\"color: #008000\">3</span>]);\n",
       "  <span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">2</span> <span style=\"color: #AA22FF; font-weight: bold\">=</span> nn<span style=\"color: #AA22FF; font-weight: bold\">.</span>conv2d(<span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">0</span>, <span style=\"color: #AA22FF; font-weight: bold\">%</span>w3, padding<span style=\"color: #AA22FF; font-weight: bold\">=</span>[<span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>], channels<span style=\"color: #AA22FF; font-weight: bold\">=</span><span style=\"color: #008000\">1</span>, kernel_size<span style=\"color: #AA22FF; font-weight: bold\">=</span>[<span style=\"color: #008000\">3</span>, <span style=\"color: #008000\">3</span>]);\n",
       "  <span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">3</span> <span style=\"color: #AA22FF; font-weight: bold\">=</span> nn<span style=\"color: #AA22FF; font-weight: bold\">.</span>conv2d(<span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">0</span>, <span style=\"color: #AA22FF; font-weight: bold\">%</span>w4, padding<span style=\"color: #AA22FF; font-weight: bold\">=</span>[<span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>, <span style=\"color: #008000\">1</span>], channels<span style=\"color: #AA22FF; font-weight: bold\">=</span><span style=\"color: #008000\">1</span>, kernel_size<span style=\"color: #AA22FF; font-weight: bold\">=</span>[<span style=\"color: #008000\">3</span>, <span style=\"color: #008000\">3</span>]);\n",
       "  <span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">4</span> <span style=\"color: #AA22FF; font-weight: bold\">=</span> add(<span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">1</span>, <span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">2</span>);\n",
       "  add(<span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">3</span>, <span style=\"color: #AA22FF; font-weight: bold\">%</span><span style=\"color: #008000\">4</span>)\n",
       "}\n",
       "</pre></div>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "mod.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ai",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
